<p>  Promise是js里面的一个构造函数，主要是为了用来解决以前使用回调函数嵌套多层之后导致代码难以维护和理解的问题。<br>  Promise的原型上主要有then、catch、finally方法，静态方法主要有resolve,reject,race,all,allSettled。</p>
<p>那么我们来试着分析下如下的代码吧，看看then、catch、finally是怎么用的：</p>
<pre><code class="language-JavaScript"><span class="code-block">&lt;script&gt;
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript"><span class="hljs-meta">
'use strict'</span>;
<span class="hljs-keyword">let</span> testRun=<span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'testRun:'</span>+data)
}
<span class="hljs-comment">/* 第一个setTimeout */</span>
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
    <span class="hljs-comment">/* 第一个Promise */</span>
    <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">resolve</span>().<span class="hljs-title function_">then</span>(<span class="hljs-function">()=&gt;</span>{
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">15</span>);
    });
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">14</span>);
},<span class="hljs-number">100</span>);
<span class="hljs-comment">/* 第二个Promise */</span>
<span class="hljs-keyword">let</span> test=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>)=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">1</span>);
    <span class="hljs-keyword">if</span>(<span class="hljs-literal">true</span>){
        <span class="hljs-title function_">resolve</span>(<span class="hljs-number">6</span>);
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-title function_">reject</span>(<span class="hljs-number">16</span>);
    }
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">2</span>);
}).<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>){
        <span class="hljs-comment">/* 第二个setTimeout */</span>
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'推入新的宏任务队列'</span>);
    })
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'data:'</span>+data);
    <span class="hljs-keyword">return</span> data;
},testRun).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'error:'</span>+error);
}).<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">8</span>);
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(a)
}).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'error:'</span>+error);
}).<span class="hljs-title function_">finally</span>(<span class="hljs-function">()=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'finally:12'</span>);
});

<span class="hljs-comment">/* 第三个Promise */</span>
<span class="hljs-keyword">let</span> promise2=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>)=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">3</span>);
    <span class="hljs-keyword">if</span>(<span class="hljs-literal">true</span>){
        <span class="hljs-title function_">resolve</span>(<span class="hljs-number">7</span>);
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-title function_">reject</span>(<span class="hljs-number">27</span>);
    }
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">4</span>);
}).<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Promise2data:'</span>+data);
    <span class="hljs-keyword">return</span> data;
},testRun).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Promise2error:'</span>+error);
}).<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">9</span>);
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(a)
}).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Promise2error:'</span>+error);
}).<span class="hljs-title function_">finally</span>(<span class="hljs-function">()=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Promise2finally:13'</span>);
});

<span class="hljs-comment">/* 第三个setTimeout */</span>
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>){
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">16</span>);
},<span class="hljs-number">100</span>);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-number">5</span>);
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
<span class="hljs-comment">/* 运行结果 */</span>
<span class="hljs-number">1</span>
<span class="hljs-number">2</span>
<span class="hljs-number">3</span>
<span class="hljs-number">4</span>
<span class="hljs-number">5</span>
<span class="hljs-attr">data</span>:<span class="hljs-number">6</span>
<span class="hljs-title class_">Promise2</span><span class="hljs-attr">data</span>:<span class="hljs-number">7</span>
<span class="hljs-number">8</span>
<span class="hljs-number">9</span>
<span class="hljs-attr">error</span>:<span class="hljs-title class_">ReferenceError</span>: a is not defined
<span class="hljs-title class_">Promise2</span><span class="hljs-attr">error</span>:<span class="hljs-title class_">ReferenceError</span>: a is not defined
<span class="hljs-attr">finally</span>:<span class="hljs-number">12</span>
<span class="hljs-title class_">Promise2</span><span class="hljs-attr">finally</span>:<span class="hljs-number">13</span>
推入新的宏任务队列
<span class="hljs-number">14</span>
<span class="hljs-number">15</span>
<span class="hljs-number">16</span></span>
</code><span class="copy-button">复制代码</span></pre>
<p>  我们知道js是按照从上到下依次执行的，那么这里依次从上到下的顺序我们来理一下：</p>
<ol>
<li>  1、先定义了一个匿名函数并将这个匿名函数赋值给了testRun。</li><li>  2、执行了第一个setTimeout函数，设置了一个定时器，在100毫秒之后将setTimeout里面的匿名函数推入宏任务队列之中。</li><li>  3、第二个Promise构造函数处使用Promise新建一个Promise对象，赋值给了test变量。执行Promise构造函数内的代码，即：console.log(1)，打印了1；执行了resolve函数，将第二个Promise的状态由pending改为fulfilled并将第一个then方法内回调函数推入微任务队列,并返回新的Promise对象；console.log(2)，打印了2.</li><li>  4、第三个Promise构造函数处使用Promise新建一个Promise对象，赋值给了promise2变量。执行Promise构造函数内的代码，即：console.log(3)，打印了3；执行了resolve函数，将第三个Promise的状态由pending改为fulfilled并将第一个then方法内回调函数推入微任务队列,并返回新的Promise对象；console.log(4)，打印了4.</li><li>  5、执行了第三个setTimeout函数，设置了一个定时器，在100毫秒之后将setTimeout里面的匿名函数推入宏任务队列之中。</li><li>  6、执行了console.log(5);打印了5。此时，第一次加入宏任务队列的函数就执行完了。</li><li>  7、循环微任务队列，执行了第二个Promise构造函数的then的回调函数，执行了第二个setTimeout函数，设置了一个定时器，在引擎允许的最小时间后将其回调函数推入宏任务队列；将新的Promise对象状态改为fulfilled，再将第二个then方法回调函数推入微任务队列。执行了第三个Promise构造函数的then的回调函数，与第二个Promise构造函数情况类似。。。如此，将Promise后面的then、catch、finally方法依次执行又放下一个回调入微任务队列，直至执行完。</li><li>  8、第二个setTimeout函数的回调函数被推入宏任务队列后被加入函数调用栈执行。</li><li>  9、100毫秒后，第一个setTimeout的回调函数被推入宏任务队列后被加入函数调用栈执行,Promise.resolve().then仍然是异步的，即先将then的回调函数加入微任务队列，待执行了同步的console.log(14);之后再执行的then的回调函数。</li><li>  10、第三个setTimeout的回调函数被推入宏任务队列后被加入函数调用栈执行。</li></ol>
<p>  我们看到Promise仍然是需要使用回调函数的方式的，只是把以前我们需要嵌套执行的回调嵌套改成了我们可以链式调用的方式，这样可以使得我们更容易理解和阅读程序的逻辑。其中需要注意的主要就以下几点：</p>
<ol>
<li>  1、使用new Promise()方式调用时，Promise构造函数内的代码是立即执行的，构造函数内异步的函数执行完成之后按照需要调用resolve成功或reject失败回调方法，而resolve回调方法会将此时的Promise状态修改为fulfilled，reject回调方法会将此时的Promise状态修改为rejected，且一旦状态修改后，状态便不能再被修改了。</li><li>  2、then、catch、finally都是异步的，是将回调加入微任务队列执行，且回调环境为当前promise,最后都返回一个新的Promise对象。</li><li>  3、then方法可以有两个回调函数，第一个用于成功回调，第二个用于失败回调，不过我们通常将失败回调放到then后的catch方法里面去处理，这样更易于阅读代码，同时catch还可以捕获then中的错误。</li><li>  4、如果then中已经处理了前面的错误或者失败，且then中回调也没有再抛出新的错误，那就不会再执行catch方法里的回调了。如果then没有处理前面的错误的回调函数，那么前面的错误或者失败回调或者then中的错误都会触发catch中的回调函数。</li><li>  5、finally则是不管当前promise状态如何都会进入，且没有参数传入。<br>  接下来我们再来说说Promise的静态方法。<br>  <strong>Promise.resolve</strong>:<br>  Promise.resolve()是接收参数的，根据参数不同，内部操作也不同，不过最终都是返回一个resolved的Promise对象。其中，如果没有参数，那就返回一个resolved状态的Promise对象，之后再接着调用then或catch也没有参数传入他们的回调函数中；参数如果是一个Promise实例对象，那么就直接返回这个实例；参数如果是一个对象，且这个对象有then方法，那么就按照resolved状态调用这个对象的then方法；参数如果不是对象或者没有then方法，那么还是返回一个resolved状态的Promise对象，且把参数传给接下来的then方法的回调函数。<br>  <strong>Promise.reject</strong>:<br>  Promise.reject()与Promise.resolve()相仿，只是是返回一个状态为rejected的Promise对象。<br>  <strong>Promise.race</strong>:<br>  Promise.race([promise1,promise2,promise3])是将多个Promise实例，包装成一个新的Promise实例，参数可以是数组，也可以是其他Iterator接口的可迭代的对象，如map、set等。可以用来当有多个异步操作，只要其中一个异步操作状态改变，那么我们这里的异步操作就要跟着改变状态时使用，且最先改变状态的那个promise的状态决定了Promise.race实例的状态。<br>  <strong>Promise.all</strong>:<br>  Promise.all([promise1,promise2,promise3])是将多个Promise实例，包装成一个新的Promise实例。可以用来当有多个异步操作，只要其中一个异步操作状态改变为rejected或者所有的异步操作状态都改变为fulfilled，那么我们这里的异步操作就要跟着改变状态时使用。<br>  <strong>Promise.allSettled</strong>:<br>  Promise.allSettled([promise1,promise2,promise3])是将多个Promise实例，包装成一个新的Promise实例。可以用来当有多个异步操作，必须要所有的异步操作都改变状态之后才执行当前的操作的情况使用，因为allSettled就是需要所有传入的异步操作都改变了状态之后才执行。和Promise.all不同的是，如果Promise.all其中的异步操作某个状态变成了rejected，那Promise.all的状态就会变成rejected返回了。不过Promise.allSettled是一个比较新的标准，目前浏览器中只有新的谷歌和火狐浏览器才支持，具体可以查看<a href="caniuse">caniuse</a>。<br>  需要注意的是race、all、allSettled方法里面，如果参数列表里面返回结果的promise状态开始时改为rejected，但是该promise本身对rejected状态进行了处理，如使用了catch处理后新返回的promise状态为fulfilled，那么返回到接收的race、all、allSettled对应的promise状态其实是最后的状态，也就是fulfilled。</li></ol>
<pre><code class="language-JavaScript"><span class="code-block">&lt;script&gt;
<span class="hljs-meta">'use strict'</span>;
<span class="hljs-keyword">let</span> p1=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve,reject</span>)=&gt;</span>{
    <span class="hljs-title function_">reject</span>(<span class="hljs-number">11</span>);
}).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(data);
    <span class="hljs-keyword">return</span> <span class="hljs-number">12</span>;
}).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'p1'</span>,error);
    <span class="hljs-keyword">return</span> <span class="hljs-number">13</span>;
});
<span class="hljs-keyword">let</span> p2=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve,reject</span>)=&gt;</span>{
    <span class="hljs-title function_">resolve</span>(<span class="hljs-number">21</span>);
}).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(data);
    <span class="hljs-keyword">return</span> <span class="hljs-number">22</span>;
}).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'p2'</span>,error);
    <span class="hljs-keyword">return</span> <span class="hljs-number">23</span>;
});
<span class="hljs-keyword">let</span> p3=<span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([p2,p1]).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'allthen'</span>,data);
}).<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span>{
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'error'</span>,error);
});
&lt;/script&gt;
<span class="hljs-comment">/* 运行结果 */</span>
<span class="hljs-number">21</span>
p1 <span class="hljs-number">11</span>
allthen (<span class="hljs-number">2</span>)&nbsp;[<span class="hljs-number">22</span>, <span class="hljs-number">13</span>]</span>
</code><span class="copy-button">复制代码</span></pre>
